﻿using RakutenProxy.DataConverter.Helper;
using RakutenProxy.Model.NopCommerce;
using RakutenProxy.Model.Rakuten;
using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace RakutenProxy.DataConverter
{
    public class RakNopProductConverter : RakNopDataConverter<RakutenProxy.Model.Rakuten.Product, RakutenProxy.Model.NopCommerce.Product>
    {
        public RakNopProductConverter(DbContext sourceDbContext, DbContext targetDbContext)
            : base(sourceDbContext, targetDbContext)
        {
        }

        public override void Migrate()
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var rakDbContext = (RakutenDbContext)SourceContext;

            var rakProducts = rakDbContext.Products.Where(r => !r.IsDelete);
            var productMaps = rakDbContext.Rakuten_NopCommerce_Product_Maps.Select(p => p.RakutenProductId);
            var notMappedProducts = rakProducts.Where(r => !productMaps.Contains(r.ProductId));

            var mappedProducts = rakProducts.Except(notMappedProducts);

            // Insert not mapped products
            foreach (var product in notMappedProducts)
            {
                Picture picture = null;

                if (product.ThumbnailImage != null)
                {
                    picture = new Picture { IsNew = true, MimeType = "image/jpeg", PictureBinary = product.ThumbnailImage };
                    nopDbContext.Pictures.Add(picture);
                }


                var nopProduct = GetObjectFromSource(product);
                nopDbContext.Products.Add(nopProduct);
                nopDbContext.SaveChanges();

                var categoryMap = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == product.CategoryId);
                if (categoryMap != null)
                {
                    var nopProductCategoryMapping = new Model.NopCommerce.Product_Category_Mapping { CategoryId = categoryMap.NopCommerceCategoryId, ProductId = nopProduct.Id };
                    nopDbContext.Product_Category_Mapping.Add(nopProductCategoryMapping);
                }

                var urlRecord = AddOrUpdateUrlRecord(nopProduct);
                if (urlRecord.Id == 0)
                {
                    nopDbContext.UrlRecords.Add(urlRecord);
                }

                //BackgrounDownloadPicture(new BackgroundImageDownloadModel { NopProductId = nopProduct.Id, ImageUrl = product.Thumbnail });

                if (picture != null)
                {
                    var productPictureMapping = new Model.NopCommerce.Product_Picture_Mapping { PictureId = picture.Id, ProductId = nopProduct.Id, DisplayOrder = 1 };
                    nopDbContext.Product_Picture_Mapping.Add(productPictureMapping);
                }

                var mapping = new Rakuten_NopCommerce_Product_Map { NopCommerceProductId = nopProduct.Id, RakutenProductId = product.ProductId };
                rakDbContext.Rakuten_NopCommerce_Product_Maps.Add(mapping);
            }

            // Update nop product if something changed.
            foreach (var product in mappedProducts)
            {
                var map = rakDbContext.Rakuten_NopCommerce_Product_Maps.FirstOrDefault(p => p.RakutenProductId == product.Price);
                if (map != null)
                {
                    var nopProduct = nopDbContext.Products.FirstOrDefault(p => p.Id == map.NopCommerceProductId);
                    if (nopProduct != null)
                    {
                        if (!product.Equals(nopProduct))
                        {
                            UpdateObjectFromSource(nopProduct, product);
                        }
                    }
                }
            }

            rakDbContext.SaveChanges();
            nopDbContext.SaveChanges();
        }

        private string TruncateString(string input, int maxLength)
        {
            if (string.IsNullOrEmpty(input))
                return input;
            if (input.Length <= maxLength)
                return input;
            return input.Substring(0, maxLength);
        }

        /// <summary>
        /// Return the Nop product based on rakuten product
        /// </summary>
        /// <param name="rakProduct"></param>
        /// <returns></returns>
        protected override Model.NopCommerce.Product GetObjectFromSource(Model.Rakuten.Product source)
        {
            var nopProduct = new RakutenProxy.Model.NopCommerce.Product();
            nopProduct.ProductTemplateId = (int)ProductTemplateId.Simple;
            nopProduct.ProductTypeId = (int)ProductTypeId.Simple;
            nopProduct.VisibleIndividually = true;
            nopProduct.Name = TruncateString(Translate(source.ProductName), 400);
            nopProduct.ShortDescription = Translate(source.ShortDescription);
            nopProduct.FullDescription = Translate(source.DetailHtml);
            nopProduct.AllowCustomerReviews = true;
            nopProduct.IsShipEnabled = true;
            nopProduct.ManageInventoryMethodId = (int)ManageInventoryMethod.DontManageStock;
            nopProduct.StockQuantity = 1000;
            nopProduct.DisplayStockAvailability = true;
            nopProduct.DisplayStockQuantity = false;
            nopProduct.NotifyAdminForQuantityBelow = 1;
            nopProduct.OrderMinimumQuantity = 1;
            nopProduct.OrderMaximumQuantity = 1000;
            nopProduct.AllowedQuantities = TruncateString(source.AllowedQuantity, 1000);
            nopProduct.Price = source.Price;
            nopProduct.Published = true;
            nopProduct.CreatedOnUtc = DateTime.UtcNow;
            nopProduct.UpdatedOnUtc = DateTime.UtcNow;

            return nopProduct;
        }

        protected override void UpdateObjectFromSource(Model.NopCommerce.Product target, Model.Rakuten.Product source)
        {
            target.Price = source.Price;
            target.Name = Translate(source.ProductName);
            target.AllowedQuantities = source.AllowedQuantity;
            target.ShortDescription = Translate(source.ShortDescription);
            target.FullDescription = Translate(source.DetailHtml);
            target.UpdatedOnUtc = DateTime.UtcNow;
        }

        private UrlRecord AddOrUpdateUrlRecord(Model.NopCommerce.Product product)
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var slug = Utils.UniformUrl(product.Name);
            var urlRecord = nopDbContext.UrlRecords.FirstOrDefault(u => u.Slug == slug);
            if (urlRecord == null)
            {
                urlRecord = new UrlRecord { EntityId = product.Id, EntityName = product.GetType().Name, Slug = Utils.UniformUrl(product.Name), IsActive = true };
            }
            else
            {
                urlRecord.EntityId = product.Id;
            }

            return urlRecord;
        }

        /// <summary>
        /// Migrate one product from RakDbContext to NopDbContext
        /// </summary>
        /// <param name="id"></param>
        public override void MigrateOneRecord(int id)
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var rakDbContext = (RakutenDbContext)SourceContext;

            var rakProduct = rakDbContext.Products.FirstOrDefault(p => p.ProductId == id);
            if (rakProduct == null)
                return;

            var map = rakDbContext.Rakuten_NopCommerce_Product_Maps.FirstOrDefault(m => m.RakutenProductId == id);
            if (map == null) // has not mapped
            {
                Picture picture = null;

                if (rakProduct.ThumbnailImage != null)
                {
                    picture = new Picture { IsNew = true, MimeType = "image/jpeg", PictureBinary = rakProduct.ThumbnailImage };
                    nopDbContext.Pictures.Add(picture);
                }

                // add new product
                var nopProduct = GetObjectFromSource(rakProduct);
                nopDbContext.Products.Add(nopProduct);
                nopDbContext.SaveChanges();

                // map category of product
                var categoryMap = rakDbContext.Rakuten_NopCommerce_Category_Maps.FirstOrDefault(m => m.RakutenCategoryId == rakProduct.CategoryId);
                if (categoryMap != null)
                {
                    var nopProductCategoryMapping = new Model.NopCommerce.Product_Category_Mapping { CategoryId = categoryMap.NopCommerceCategoryId, ProductId = nopProduct.Id };
                    nopDbContext.Product_Category_Mapping.Add(nopProductCategoryMapping);
                }

                var urlRecord = AddOrUpdateUrlRecord(nopProduct);
                if (urlRecord.Id == 0)
                {
                    nopDbContext.UrlRecords.Add(urlRecord);
                }
               
                // map product picture
                if (picture != null)
                {
                    var productPictureMapping = new Model.NopCommerce.Product_Picture_Mapping { PictureId = picture.Id, ProductId = nopProduct.Id, DisplayOrder = 1 };
                    nopDbContext.Product_Picture_Mapping.Add(productPictureMapping);
                }

                // save the mapped info
                map = new Rakuten_NopCommerce_Product_Map { NopCommerceProductId = nopProduct.Id, RakutenProductId = rakProduct.ProductId };
                rakDbContext.Rakuten_NopCommerce_Product_Maps.Add(map);
                rakDbContext.SaveChanges();

                nopDbContext.SaveChanges();
            }
            else
            {
                var nopProduct = nopDbContext.Products.First(p => p.Id == map.NopCommerceProductId);
                UpdateObjectFromSource(nopProduct, rakProduct);
                nopDbContext.SaveChanges();
            }
        }

        public override void RemoveOneRecord(int id)
        {
            var nopDbContext = (NopCommerceEntities)TargetContext;
            var rakDbContext = (RakutenDbContext)SourceContext;

            var map = rakDbContext.Rakuten_NopCommerce_Product_Maps.FirstOrDefault(m => m.RakutenProductId == id);
            if (map == null)
                return;

            var nopProduct = nopDbContext.Products.FirstOrDefault(p => p.Id == map.NopCommerceProductId);
            if (nopProduct != null)
            {
                nopDbContext.Products.Remove(nopProduct);
            }

            rakDbContext.Rakuten_NopCommerce_Product_Maps.Remove(map);

            rakDbContext.SaveChanges();
            nopDbContext.SaveChanges();
        }

        private void BackgrounDownloadPicture(BackgroundImageDownloadModel model)
        {
            var backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += backgroundWorker_DoWork;
            backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;

            backgroundWorker.RunWorkerAsync(model);
        }

        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
                return;

            var model = (BackgroundImageDownloadModel)e.Result;
            if (model == null || model.DownloadedPicture == null)
                return;

            var picture = model.DownloadedPicture;
            var nopDbContext = (NopCommerceEntities)TargetContext;
            nopDbContext.Pictures.Add(picture);
            nopDbContext.SaveChanges();

            var pictureMap = new Product_Picture_Mapping { PictureId = picture.Id, ProductId = model.NopProductId };
            nopDbContext.Product_Picture_Mapping.Add(pictureMap);
            nopDbContext.SaveChanges();
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var model = (BackgroundImageDownloadModel)e.Argument;

            Picture picture = null;
            if (!string.IsNullOrEmpty(model.ImageUrl))
            {
                var imageData = ImageDownloader.Download(model.ImageUrl);
                if (imageData.Length > 0)
                {
                    picture = new Picture { IsNew = true, MimeType = "image/jpeg", PictureBinary = imageData };
                    model.DownloadedPicture = picture;
                }
            }

            e.Result = model;
        }

        class BackgroundImageDownloadModel
        {
            public string ImageUrl { get; set; }
            public int NopProductId { get; set; }

            public Picture DownloadedPicture { get; set; }
        }

    }
}